<template>
	<view class="vi_countdown">
		<slot>
			<text v-if="!useTagStyle">{{showValue}}</text>
			<!-- tag样式 -->
			<view class="vi_fbox" v-else-if="displayArr.length">
				<view class="vi_countdown_space" v-for="(item,index) in displayArr" :key="index"
					v-if="!item.condition||(currValue[item.condition]>item.checkVal)">
					<text v-if="item.type=='name'">{{item.value}}</text>
					<vi-tag v-else-if="item.type=='value'"
						:type="tagSetting.type"
						:color="tagSetting.color"
						:textColor="tagSetting.textColor"
						:borderColor="tagSetting.borderColor"
						:backgroundColor="tagSetting.backgroundColor"
						:radius="tagSetting.radius"
						:size="tagSetting.size"
						:shadow="tagSetting.shadow"
						:borderWidth="tagSetting.borderWidth"
						:borderType="tagSetting.borderType"
						:borderBox="tagSetting.borderBox"
						:plain="tagSetting.plain"
						:bold="tagSetting.bold"
						:fontSize="tagSetting.fontSize">
						<text>{{computeVal(item,currValue[item.value])}}</text>
					</vi-tag>
				</view>
			</view>
		</slot>
	</view>
</template>

<script>
	export default{
		name:"viCountdown",
		props:{
			//目标时间，必须是有效日期时间格式。暂停恢复后不会影响目标时间
			target:{
				type:[Number,String,Date,null],
				default:null
			},
			//倒计时长，暂停恢复后会以当前时间进行补量。与目标时间二选一，目标时间优先
			time:{
				type:[Number,String,null],
				default:null
			},
			//开启毫秒计算，未开启则以秒计算
			useMillisecond:{
				type:[Boolean,null],
				default:false
			},
			//毫秒延迟计算
			millisecondDelay:{
				type:[Number,String,null],
				default:null
			},
			//自动开始
			autoStart:{
				type:[Boolean,null],
				default:false
			},
			//倒计时结束后使用结束文本显示
			useFinishText:{
				type:[Boolean,null],
				default:false
			},
			//显示格式
			format:{
				type:[String,null],
				default:""
			},
			//使用tag样式
			useTagStyle:{
				type:[Boolean,null],
				default:false
			},
			tagSet:{
				type:[Object,null],
				default:()=>({})
			},
			text:{
				type:[Object,null],
				default:()=>({})
			}
		},
		data(){
			return {
				inTarget:0,		//目标时间，时间戳
				leftover:0,		//剩余计数
				showValue:"",
				timer:null,
				isTarget:false,	//指定目标时间
				isPause:false,	//暂停状态
				dicText:{
					unitDay:"天",
					finish:"已结束"
				},
				//tag样式
				dicTagSet:{
					type:"primary"
				},
				currValue:{
					day:0,
					hour:0,
					minute:0,
					second:0,
					millisecond:0,
				},
				displayArr:[],
			}
		},
		computed:{
			textSetting(){
				if(this.$api.isObject(this.text)){
					return Object.assign({},this.dicText,this.text)
				}
				return this.dicText
			},
			tagSetting(){
				if(this.$api.isObject(this.tagSet)){
					return Object.assign({},this.dicTagSet,this.tagSet)
				}
				return this.dicTagSet
			},
		},
		methods:{
			computeVal(item,val){
				let diff = item.full - String(val).length,
					res = val;
				for(let i=0;i<diff;i++){
					res = "0"+res
				}
				return res
			},
			//开始倒计时，开始后目标时间锁定，变更目标时间需重新开始
			start(){
				let now = Date.now();
				//暂停状态，重新计算目标时间后执行
				if(this.isPause){
					//如果非指定目标时间，则进行补量
					if(!this.isTarget){
						this.inTarget = now + this.leftover;
					}
					this.isPause = false;
					this.init();
					return
				}
				//初始化目标时间
				let time = Number(this.time) ||0,
					target = this.target;
				this.isTarget =false;
				this.isPause = false;
				this.inTarget = time? now+time :0;
				if(target){//存在目标时间则进行覆盖
					if(this.$api.isNumber(target)){
						target=Number(target)
					}
					let targetNum = new Date(target).getTime();
					if(targetNum&&targetNum>now){
						this.inTarget = targetNum;
						this.isTarget = true;
					}
				}
				//目标值小于等于0，为已完成
				if(this.inTarget<=0){
					this.$emit("done");
					return
				}
				this.init();
			},
			//暂停，已启动的状态下才能暂停
			pause(){
				if(this.timer){
					clearInterval(this.timer);
					this.timer = null;
					this.isPause =true;
				}
			},
			//初始化，创建计时器
			init(){
				//tag样式
				if(this.useTagStyle){
					//从格式字串提取
					if(this.format){
						this.displayArr =[];
						let format = String(this.format),
							displayArr = this.displayArr,
							splitArr = format.split(/([Dhms]+)/g).filter((val)=>{
								return !!val
							});
						for(let val of splitArr){
							let type = ["DD","D","hh","h","mm","m","ss","s","ms"].includes(val)? "value" :"name",
								value,
								full = ["hh","mm","ss"].includes(val)?2:(val=="ms"?3:0);
							switch(val){
								case "DD":
								case "D":
									value = "day";
									break;
								case "hh":
								case "h":
									value = "hour";
									break;
								case "mm":
								case "m":
									value = "minute";
									break;
								case "ss":
								case "s":
									value = "second";
									break;
								case "ms":
									value = "millisecond";
									break;
								default:
									value = val;
							}
							displayArr.push({
								type,
								value,
								full
							})
						}
					}else{
						this.displayArr =[
							{type:"value", value:"day", full:0, condition:"day", checkVal:86400000},
							{type:"name", value:this.textSetting.unitDay, condition:"day", checkVal:86400000},
							{type:"value", value:"hour", full:2, condition:"hour", checkVal:3600000},
							{type:"name", value:":", condition:"hour", checkVal:3600000},
							{type:"value", value:"minute", full:2},
							{type:"name", value:":"},
							{type:"value", value:"second", full:2},
						]
						if(this.useMillisecond){
							this.displayArr.push(
								{type:"name", value:":"},
								{type:"value", value:"millisecond", full:3}
							)
						}
					}
				}
				if(this.timer){
					clearInterval(this.timer)
				}
				let delay = 1000;
				if(this.useMillisecond){
					let millisecondDelay = this.millisecondDelay;
					delay = millisecondDelay&&this.$api.isNumber(millisecondDelay)? Number(millisecondDelay) :35
				}
				this.draw();
				this.timer = setInterval(()=>{
					this.draw();
					if(this.leftover<=0){//结束
						clearInterval(this.timer);
						this.timer = null;
						this.$emit("done")
					}
				},delay)
			},
			//绘制
			draw(){
				let now = Date.now(),
					diff = this.inTarget - now,
					res = this.format;
					diff<0? diff=0 :"";
				let D = parseInt(diff/86400000),
					H = parseInt(diff%86400000/3600000),
					M = parseInt(diff%3600000/60000),
					S = parseInt(diff%60000/1000),
					MS = diff%1000;
				if(this.useTagStyle){
					this.currValue = {
						day:D,
						hour:H,
						minute:M,
						second:S,
						millisecond:MS,
					}
				}else if(res){
					res = res.replace(/DD/, D);
					res = res.replace(/D/, D);
					res = res.replace(/hh/, (H<10?"0":"")+H);
					res = res.replace(/h/, H);
					res = res.replace(/mm/, (M<10?"0":"")+M);
					res = res.replace(/m/, M);
					res = res.replace(/ss/, (S<10?"0":"")+S);
					res = res.replace(/s/, S);
					res = res.replace(/ms/, (MS<10?"00":(MS<100?"0":""))+MS);
				}else{//默认格式，分、秒必定显示
					res ="";
					diff>86400000? res+=`${D+this.textSetting.unitDay} ` :"";
					diff>3600000? res+=`${(H<10?"0":"")+H}: ` :"";
					res+=`${(M<10?"0":"")+M}: `;
					res+=`${(S<10?"0":"")+S}`;
					this.useMillisecond? res+=`: ${(MS<10?"00":(MS<100?"0":""))+MS}` :"";
				}
				this.showValue = res;
				this.leftover = diff;
				if(diff<=0&&this.useFinishText){
					this.showValue=this.textSetting.finish
				}
				this.$emit("change",{
					day: D,
					hour: H,
					minute: M,
					second: S,
					millisecond: MS,
					text: this.showValue
				})
			}
		},
		watch:{
			"target":{
				handler(newValue){
					if(this.autoStart){
						this.start()
					}
				}
			},
			"time":{
				handler(newValue){
					if(this.autoStart){
						this.start()
					}
				}
			},
		},
		created(){
			if(this.autoStart){
				this.start()
			}
		},
		beforeDestroy(){
			if(this.timer){
				clearInterval(this.timer);
				this.timer = null;
			}
		}
	}
</script>

<style lang="scss">
.vi_countdown{
	.vi_fbox{
		display: flex;
		justify-content: center;
		align-items: center;
	}
	.vi_countdown_space{
		margin: 0 0.25em;
	}
}
</style>